/*
 * @Author: rock
 * @Date: 2025-04-17 11:38:12
 * @LastEditors: rock
 * @LastEditTime: 2025-04-17 17:27:10
 * @FilePath: /004epoll/main/main.cpp
 * @Description:
 *
 * Copyright (c) 2025 by rock, All Rights Reserved.
 */

#include <iostream>
#include "initsock.h"

// server main
int main(int argc, const char *argv[])
{
    // std::cout << "argc : " << argc << std::endl;
    // for (int i = 0; i<argc; i++)
    // {
    //     std::cout << "argv : " << argv[i] << std::endl;
    // }

    // 初始化服务器监听套接字
    int lfd = 0;
    initsock(lfd);

    // 初始化epoll, 创建epoll实例
    // 所有文件描述符对应读写缓冲区状态都是委托内核进行检测
    int epfd = epoll_create(100);
    if (-1 == epfd)
    {
        err_str_out_("epfd err");
        exit(0);
    }

    // 向epoll实例中添加需要检测的节点, 现在只有监听文件描述符
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = lfd;
    // 把lfd添加到epoll实例中
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if (-1 == ret)
    {
        err_str_out_("epoll_ctl");
        exit(-1);
    }

    struct epoll_event evs[1024];
    int size = sizeof(evs) / sizeof(struct epoll_event);
    // 开始检测
    while (1)
    {
        // 调用wait一次检测一次, 返回的num是检测到的文件描述符变化的个数
        int num = epoll_wait(epfd, evs, size, -1);
        for (int i = 0; i < num; i++)
        {
            // 获取当前最前面的文件描述符
            int curfd = evs[i].data.fd;
            // 看看是不是监听文件描述符
            if (curfd == lfd)
            {
                // 建立新连接
                int cfd = accept(curfd, NULL, NULL);
                // 把新连接的文件描述符添加到epoll实例中, 以便下一次循环可以检测这个文件描述符
                ev.events = EPOLLIN;
                ev.data.fd = cfd;
                ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
                if (-1 == ret)
                {
                    err_str_out_("accept!");
                    exit(-1);
                }
            }
            else
            { // 通信
                // 处理通信文件描述符
                // 接收数据
                char buff[1024];
                memset(buff, 0, sizeof(buff));
                int len = recv(curfd, buff, sizeof(buff), 0);
                if (0 == len)
                {
                    std::cout << "客户端断开连接" << std::endl;
                    // 把这个客户端从epoll实例中删除, 要先删除实例, 再关闭套接字, 否则epoll_ctl要返回-1;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, nullptr);
                    // 关闭套接字
                    close(curfd);
                }
                else if (len > 0)
                {
                    std::cout << "client say : " << buff << std::endl;
                    send(curfd, buff, sizeof(buff), 0);
                }
                else
                {
                    err_str_out_("recv");
                    exit(-1);
                }
            }
        }
    }

    // 关闭通信套接字
    close(lfd);

    return 0;
}
